use std::sync::RwLock;

use crate::memory::{memory_write_bytes, PAGE_COUNT};
use crate::address::{PhysAddr, PhysPageNum};
use crate::PAGE_SIZE;

use lazy_static::*;

pub fn alloc_page() -> PhysPageNum {
    MANAGER.write().unwrap().alloc_page().unwrap()
}   

pub fn dealloc_page(ppn: PhysPageNum) {
    MANAGER.write().unwrap().dealloc_page(ppn);
}

lazy_static! {
    static ref MANAGER: RwLock<PageManager> = {
        RwLock::new(PageManager::new(PAGE_COUNT))
    };
}

struct PageManager {
    current: usize,
    end: usize,
    recycled: Vec<usize>,
}

impl PageManager {
    fn new(end: usize) -> Self {
        Self {
            current: 0,
            end,
            recycled: Vec::new()
        }
    }

    fn alloc_page(&mut self) -> Option<PhysPageNum> {
        if let Some(index) = self.recycled.pop() {
            Some(index.into())
        } else if self.current == self.end {
            None
        } else {
            let index = self.current;
            self.current += 1;
            Some(index.into())
        }
    }

    fn dealloc_page(&mut self, ppn: PhysPageNum) {
        let index = ppn.into();
        if index >= self.current || self.recycled.iter().any(|&rindex| { rindex == index }){
            panic!("Try to dealloc a unalloced page number {}", index);
        }

        memory_write_bytes(PhysAddr::num_and_offset(ppn, 0) , &[0u8; PAGE_SIZE]);
        self.recycled.push(index);
    }
}